// functions to help read the ADC.

/*
 Required includes:	<stdlib.h>
 					<avr/io.h>
					<stdint.h>
					<util/delay.h>
					"dac.h"
 */

/*
 Copyright 2010 Anthony Oko, Koby Hitchen, Mara Cairo, Shaochen Xu, Tyler Lucas
 
 This file is part of the trajctrl program.
 
 trajctrl is licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at
 
 http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

#include "adc.h"

// initializes ADC into free-running mode
void init_adcfree(uint8_t pre, uint8_t chan, bool useTimer)
{
	DDRA &= 0x00;									// set port A as input
	PORTA &= 0x00;									// tri-state / high impedance
	ADCSRA = pre;
	ADMUX = chan | (1<<ADLAR);						// set channel, left-adj., & AREF
	ADCSRA |= (1<<ADATE);							// auto-triggering
	SFIOR &= ~((1<<ADTS2)|(1<<ADTS1)|(1<<ADTS0));	// Free-Running mode
	if (useTimer)
		SFIOR |= (1<<ADTS1)|(1<<ADTS0);				// Timer0 Compare mode    *** CHANGE TO TIMER1 ***
	ADCSRA |= (1<<ADIE);							// enable ADC interrupt
	ADCSRA |= (1<<ADEN);
}

// updates ADC settings for next stage (already initialized)
void update_adc_free(uint8_t pre, uint8_t chan)
{
	ADMUX = chan | (1<<ADLAR);						// set channel, left-adj., & AREF
	ADCSRA = pre | (1<<ADEN) | (1<<ADATE) | (1<<ADIE);
	return;
}

// Gets values from up to 4 different channels
uint8_t *delay_adc_4chan(uint8_t chan[], uint8_t amux[], uint8_t data[], uint8_t pre, uint16_t t)
{
	PORTC = amux[0];								// set external (analog) MUX selects
	while(!(TIFR & (1<<OCF1A))) {};					// wait for timer1A flag
	TIFR |= (1<<OCF1A);
	ADCSRA |= (1<<ADSC);							// start 1st conversion
	
	uint8_t i=0;
	do {
		_delay_loop_1(1.5*pre/3-3);			// *** THIS MAY NOT BE NECESSARY ***
		ADMUX = chan[i];
		while(!(ADCSRA & (1<<ADIF))) {};			// wait for conversion complete flag
		PORTD |= amux[i];							// change analog mux selects
		data[i] = ADCH;
	} while (++i<4);
	
	OCR1A = t;
	return data;
}

// initializes (250 kHz) timer1 and triggers it when voltage less than V is detected
uint8_t init_adc_t1(uint8_t chan, uint8_t amux, uint8_t pre, uint16_t t, uint8_t V)
{
	uint8_t Vread;
	PORTC = amux;									// set external (analog) MUX selects
	OCR1A = t;										// set timer1A compare value
	ADCSRA |= (1<<ADSC);							// start 1st conversion
	
	do {
		while(!(ADCSRA & (1<<ADIF))) {};			// wait for conversion complete flag
		if (ADCH < V)
		{
			TCCR1B |= (1<<CS11) | (1<<CS10);		// (250 kHz) timer1 starts
			Vread = ADCH;
			break;
		}
	} while (1);
	
	return Vread;
}

// infinite ADC loop, with n-element array inputs
void adc_free_dac(uint8_t chan[], uint8_t amux[], uint8_t pre, uint16_t n)
{
	PORTC = amux[0];
	ADCSRA |= (1<<ADSC);							// start 1st conversion

	while (1) {
		for(uint16_t i=0;i<n;++i)
		{
			_delay_loop_1(1.5*pre/3-3);			// *** THIS MAY NOT BE NECESSARY ***
			ADMUX = chan[i];
			while(!(ADCSRA & (1<<ADIF))) {};			// wait for conversion complete flag
			PORTD |= amux[i];							// change analog mux selects
			PORTB = dacmap(ADCH);						// output ADC reading to DAC
		}
	}
	return;
}
